package com.ruoyi.hov.service.impl;

import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.dynamic.DynamicQueryServiceImpl;
import com.ruoyi.hov.HovUtils;
import com.ruoyi.hov.entity.Hov;
import com.ruoyi.hov.entity.HovDtl;
import com.ruoyi.hov.entity.HovSearchResultSet;
import com.ruoyi.hov.mapper.HovMapper;
import com.ruoyi.hov.service.ClassBindHovService;
import com.ruoyi.hov.service.HovDtlService;
import com.ruoyi.hov.service.HovService;
import com.ruoyi.query.entity.QueryTable;
import com.ruoyi.sql.WhereEntity;
import com.ruoyi.util.DynamicUtil;
import com.ruoyi.util.ReflectUtil;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * hov主表 服务实现类
 * </p>
 *
 * @author XiaShiLong
 * @since 2021-04-01
 */
@Service
public class HovServiceImpl extends DynamicQueryServiceImpl<HovMapper, Hov> implements HovService {

    private final HovDtlService hovDtlService;

    private final ClassBindHovService classBindHovService;

    @Autowired
    public HovServiceImpl(HovDtlService hovDtlService, ClassBindHovService classBindHovService) {
        this.hovDtlService = hovDtlService;
        this.classBindHovService = classBindHovService;
    }

    /**
     * 重写保存
     *
     * @param hov <see>Hov</see>
     * @return boolean
     */
    @SneakyThrows
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(Hov hov) {
        verification(hov);
        Class<?> clz = Class.forName(hov.getClassPath());
        TableName annotation = clz.getAnnotation(TableName.class);
        hov.setTableName(annotation.value());
        hov.setClassName(clz.getSimpleName());
        hov.create();
        this.getBaseMapper().insert(hov);
        hov.getHovDtlList()
                .forEach(d -> d.setHovId(hov.getId()));
        boolean b = hovDtlService.saveBatch(hov.getHovDtlList());
        if (b) {
            HovUtils.setHovCache(hov.getId(), hov);
        }
        return b;
    }

    /**
     * 重写更新
     *
     * @param hov <see>Hov</see>
     * @return boolean
     */
    @SneakyThrows
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateById(Hov hov) {
        verification(hov);
        Class<?> clz = Class.forName(hov.getClassPath());
        TableName annotation = clz.getAnnotation(TableName.class);
        hov.setTableName(annotation.value());
        hov.update();
        this.getBaseMapper().updateById(hov);
        hovDtlService.deleteByHovId(hov.getId());
        hov.getHovDtlList()
                .forEach(
                        d -> {
                            d.setHovId(hov.getId());
                            d.setId(null);
                        }
                );
        boolean b = hovDtlService.saveBatch(hov.getHovDtlList());
        if (b) {
            HovUtils.setHovCache(hov.getId(), hov);
        }
        return b;
    }

    /**
     * 重写删除
     *
     * @param id 主键
     * @return boolean
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeById(Serializable id) {
        if (classBindHovService.existByHovId(id)) {
            throw new CustomException("已绑定,无法删除!");
        }
        this.getBaseMapper().deleteById(id);
        boolean b = hovDtlService.deleteByHovId(id);
        if (b) {
            HovUtils.clearHovCache(id);
        }
        return b;
    }

    /**
     * 获取所有hov(绑定hov时获取)
     *
     * @param searchText 查询文本
     * @return List
     */
    @Override
    public Page<Hov> getAll(int page, int size, String searchText) {
        LambdaQueryWrapper<Hov> wrapper = Wrappers.lambdaQuery();
        if (StringUtils.isNotEmpty(searchText)) {
            wrapper
                    .like(Hov::getId, searchText)
                    .or()
                    .like(Hov::getName, searchText)
                    .or()
                    .like(Hov::getClassPath, searchText)
                    .or()
                    .like(Hov::getTableName, searchText);
        }
        return this.getBaseMapper().findAll(new Page<>(page, size), wrapper);
    }

    /**
     * hov查询
     *
     * @param hovId      hovId
     * @param searchText 查询文本
     * @return <see>HovSearchResultSet</see>
     */
    @Override
    public HovSearchResultSet hovSearch(int page, int size, Long hovId, String searchText) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Hov hovCache = HovUtils.getHovCache(hovId);
        Map<Integer, List<HovDtl>> scenarioMap = hovCache.getHovDtlList().stream()
                .collect(Collectors.groupingBy(HovDtl::getUseScenario));
        return HovSearchResultSet.builder()
                .showList(getShowList(scenarioMap))
                .targetShowList(getTargetShowList(scenarioMap))
                .resultSet(hovExecute(page, size, hovCache, scenarioMap, searchText))
                .hov(hovCache)
                .build();
    }

    @Override
    public Hov getById(Serializable id) {
        return this.getBaseMapper().getById(id);
    }

    /**
     * 执行hov查询
     */
    private Object hovExecute(int page, int size, Hov hov, Map<Integer, List<HovDtl>> scenarioMap, String searchText) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class<?> clz = ReflectUtil.getClass(hov.getClassPath());
        String sql = WHERE(QueryTable.sql(QueryTable.create(clz, true, void.class)), scenarioMap.get(1), scenarioMap.get(3), searchText);
        String ruoYiClassPath = "common.core.domain.entity";
        String targetClassPath;
        String replacementClassPath;
        String classPathFormat;
        if (hov.getClassPath().contains(ruoYiClassPath)) {
            targetClassPath = ruoYiClassPath;
            replacementClassPath = "system.service";
            classPathFormat = "I%sService";
        } else {
            targetClassPath = "entity";
            replacementClassPath = "service";
            classPathFormat = "%sService";
        }
        String servicePath = hov.getClassPath().replace(targetClassPath, replacementClassPath).replace(clz.getSimpleName(), String.format(classPathFormat, clz.getSimpleName()));
        Class<?> service = ReflectUtil.getClass(servicePath);
        Method hovQuery = service.getMethod("pageQuery", int.class, int.class, String.class);
        return hovQuery.invoke(SpringUtils.getBean(service), page, size, sql);

    }

    /**
     * 获取显示集合
     */
    private List<HovDtl> getShowList(Map<Integer, List<HovDtl>> scenarioMap) {
        List<HovDtl> showList = scenarioMap.get(0);
        showList.forEach(
                s -> s.setColumnName(s.getTableName() + "_" + s.getColumnName())
        );
        return showList;
    }

    /**
     * 获取目标列显示集合
     */
    private List<HovDtl> getTargetShowList(Map<Integer, List<HovDtl>> scenarioMap) {
        List<HovDtl> targetShowList = scenarioMap.get(2);
        targetShowList.forEach(
                s -> s.setColumnName(s.getTableName() + "_" + s.getColumnName())
        );
        return targetShowList;
    }

    /**
     * 拼接sql条件
     */
    private String WHERE(String sql, List<HovDtl> searchList, List<HovDtl> searchDefault, String searchText) {
        StringBuilder sb = new StringBuilder(sql + " WHERE 1=1 ");
        if (StringUtils.isNotEmpty(searchDefault)) {
            searchDefault.forEach(
                    s -> DynamicUtil.queryTypeWhere(new WhereEntity(s), sb)
            );
        }
        if (StringUtils.isNotEmpty(searchText) && StringUtils.isNotEmpty(searchList)) {
            String condition = "OR";
            sb.append(" AND (");
            searchList.forEach(
                    s -> {
                        String joiner = " %s = %s ";
                        String column = s.getTableName() + "." + s.getColumnName();
                        if (s.getDictQueryType().equals(1)) {
                            sb.append(String.format(joiner, column, "\"" + searchText + "\"")).append(condition);
                        } else {
                            joiner = " %s LIKE %s ";
                            sb.append(String.format(joiner, column, "\"%" + searchText + "%\"")).append(condition);
                        }
                    }
            );
            sb.delete(sb.toString().length() - condition.length(), sb.toString().length());
            sb.append(" )");
        }
        return sb.toString();
    }

    //验证
    private void verification(Hov hov) {
        Map<Integer, List<HovDtl>> listMap = hov.getHovDtlList()
                .stream()
                .collect(Collectors.groupingBy(HovDtl::getUseScenario));
        if (null == listMap.get(0) || listMap.get(0).isEmpty()) {
            throw new CustomException("展示列不能为空!");
        }
        if (null == listMap.get(2) || listMap.get(2).isEmpty()) {
            throw new CustomException("目标列显示不能为空!");
        }
    }
}
